package hip.util.db;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Loanin
 * @date 2014年7月25日
 * @describe
 */
public class JdbcTemplateFactory {

    public static Logger logger = Logger.getLogger(JdbcTemplateFactory.class);

    protected static Map<String, JdbcTemplate> MAP = new ConcurrentHashMap<String, JdbcTemplate>();

    protected static Map<String, NamedParameterJdbcTemplate> NAMEDMAP = new ConcurrentHashMap<String, NamedParameterJdbcTemplate>();

    public synchronized static JdbcTemplate getTestJdbcTemplate(DataSourceEntity entity) {
        JdbcTemplate template = new JdbcTemplate(generalDataSource(entity));
        return template;
    }

    public synchronized static JdbcTemplate getJdbcTemplate(DataSourceEntity entity, boolean isNew) {
        DataSource generalDataSource = generalDataSource(entity);
        String id = buildDSKey(entity);
        if (!isNew && MAP.containsKey(id)) {
            return MAP.get(id);
        }
        JdbcTemplate template = new JdbcTemplate(generalDataSource);
        MAP.put(id, template);
        return template;
    }


    public synchronized static NamedParameterJdbcTemplate getNamedJdbcTemplate(DataSourceEntity entity) {
        return getNamedJdbcTemplate(entity, false);
    }

    public synchronized static NamedParameterJdbcTemplate getNamedJdbcTemplate(DataSourceEntity entity, boolean isNew) {
        String id = buildDSKey(entity);
        if (!isNew && NAMEDMAP.containsKey(id)) {
            return NAMEDMAP.get(id);
        }
        NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(generalDataSource(entity));
        NAMEDMAP.put(id, template);
        return template;
    }

    public synchronized static JdbcTemplate getJdbcTemplate(DataSourceEntity entity) {
        return getJdbcTemplate(entity, false);
    }

    public static DataSource generalDataSource(DataSourceEntity entity) {
        if (entity.getDataBaseType() != null && !entity.getDataBaseType().isEmpty()) {
            int databaseType = Integer.valueOf(entity.getDataBaseType());
            entity.setDriver(DataSourcePingConstants.DRIVERS[databaseType]);

            if (StringUtils.isEmpty(entity.getUrl())) {
                String ip = entity.getDataBaseIp();
                String instance = StringUtils.EMPTY;
                if (ip.indexOf("\\") > -1) {
                    ip = StringUtils.substringBefore(entity.getDataBaseIp(), "\\");
                    instance = "instance=" + StringUtils.substringAfter(entity.getDataBaseIp(), "\\") + ";";
                }
                if (entity.getDataBasePort() != null && !entity.getDataBasePort().trim().equals("")) {
                    if (databaseType == 1 && StringUtils.isNotEmpty(entity.getServerName())) {
                        entity.setUrl(DataSourcePingConstants.SERVERURL[databaseType].replace("{IP}", ip)
                                .replace("{PORT}", entity.getDataBasePort().trim())
                                .replace("{SERVER}", entity.getServerName().trim())
                                .replace("{INSTANCE}", instance));
                    } else {
                        entity.setUrl(DataSourcePingConstants.URL[databaseType].replace("{IP}", ip)
                                .replace("{PORT}", entity.getDataBasePort().trim())
                                .replace("{DATABASE}", entity.getDataBaseName().trim())
                                .replace("{INSTANCE}", instance));
                    }
                } else {
                    if (databaseType == 1 && StringUtils.isNotEmpty(entity.getServerName()))
                        entity.setUrl(DataSourcePingConstants.SERVERURL[databaseType].replace("{IP}", ip)
                                .replace("{PORT}", DataSourcePingConstants.DEFAULTPORTS[databaseType])
                                .replace("{SERVER}", entity.getServerName().trim())
                                .replace("{INSTANCE}", instance));
                    else {
                        entity.setUrl(DataSourcePingConstants.URL[databaseType].replace("{IP}", ip)
                                .replace("{PORT}", DataSourcePingConstants.DEFAULTPORTS[databaseType])
                                .replace("{DATABASE}", entity.getDataBaseName().trim())
                                .replace("{INSTANCE}", instance));
                    }
                }
            }


            //entity.setUrl(DataSourcePingConstants.URL[databaseType].replace("{IP}", ip).replace("{DATABASE}", entity.getDataBaseName()).replace("{INSTANCE}", instance));
        } else {
            int coverDBAccessType = coverDBAccessType(entity.getUrl());
            entity.setDataBaseType(String.valueOf(coverDBAccessType));
        }

        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setName(entity.getDataBaseName());
        dataSource.setDriverClassName(entity.getDriver());
        dataSource.setUrl(entity.getUrl());
        dataSource.setUsername(entity.getUserName());
        dataSource.setPassword(entity.getPassword());
        // pool
        if (null != entity.getInitialSize()) {//初始化大小
            dataSource.setInitialSize(entity.getInitialSize());
        } else {
            dataSource.setInitialSize(5);
        }
        if (null != entity.getMaxActive()) {//连接池的最大数据库连接数。设为0表示无限制。一般把maxActive设置成可能的并发量就行了
            dataSource.setMaxActive(entity.getMaxActive());
        } else {
            dataSource.setMaxActive(100);
        }
        if (null != entity.getMinIdle()) {//最小等待(空闲)连接中的数量
            dataSource.setMinIdle(entity.getMinIdle());
        } else {
            dataSource.setMinIdle(5);
        }
        if (null != entity.getMaxWait()) {//最大等待毫秒数, 单位为 ms, 如果超过此时间将接到异常,设为-1表示无限制
            dataSource.setMaxWait(entity.getMaxWait());
        } else {
            dataSource.setMaxWait(5000);
        }
        //经郑俊杰与邵佰通沟通，maxActive原10调成100，maxWait原30000调成5000

        //检查连接的有效性
        int databaseType = Integer.parseInt(entity.getDataBaseType());
        dataSource.setValidationQuery(DataSourcePingConstants.PINGSQL[databaseType]);//用来检测连接是否有效的sql，要求是一个查询语句。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
        dataSource.setTestWhileIdle(true);//指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败, 则连接将被从池中去除.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
        dataSource.setTestOnBorrow(false);//指明是否在从池中取出连接前进行检验,如果检验失败 则从池中去除连接并尝试取出另一个. 注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串


        // PING
        dataSource.setTimeBetweenEvictionRunsMillis(1000);//在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程

        dataSource.setBreakAfterAcquireFailure(false);
        dataSource.setConnectionErrorRetryAttempts(1);

        dataSource.setRemoveAbandoned(true);//超过时间限制是否回收
        dataSource.setRemoveAbandonedTimeout(180);//超时时间；单位为秒。180秒=3分钟
        dataSource.setLogAbandoned(false);//打开abanded连接时输出错误日志


        return dataSource;
    }

    private static String buildDSKey(DataSourceEntity dsbean) {
        StringBuffer sb = new StringBuffer();
        sb.append(dsbean.getId());
        sb.append(dsbean.getDataBaseName());
        sb.append(dsbean.getDataBaseIp());
        sb.append(dsbean.getDataBaseType());
        sb.append(dsbean.getPassword());
        sb.append(dsbean.getUrl());
        sb.append(dsbean.getUserName());
        sb.append(dsbean.getDataBasePort());
        sb.append(dsbean.getServerName());
        return sb.toString();
    }

    public static int coverDBAccessType(String url) {
        int DBAccessType = 1;
        try {
            String driver = url.toUpperCase();
            if (driver.contains("ORACLE")) {
                DBAccessType = 1;
            } else if (driver.contains("MYSQL")) {
                DBAccessType = 2;
            } else if (driver.contains("SQLSERVER")) {
                DBAccessType = 3;
            } else if (driver.contains("DB2")) {
                DBAccessType = 4;
            } else if (driver.contains("SYBASE")) {
                DBAccessType = 5;
            } else if (driver.contains("POSTGRESQL")) {
                DBAccessType = 6;
            } else if (driver.contains("DM")) {
                DBAccessType = 7;
            } else if (driver.contains("CACHE")) {
                DBAccessType = 8;
            }
        } catch (Exception e) {
            logger.error("获取数据源配置属性失败：" + e.getMessage());
        }
        return DBAccessType;
    }

}
